#ifndef __ofxONI_h__
#define __ofxONI_h__

#include "ofMain.h"
#include <XnOpenNI.h>
#include <XnCodecIDs.h>
#include <XnCppWrapper.h>
#include "ofxOpenCv.h"
#include <string>
#include "ofxDirList.h"
#include "ofxVectorMath.h"
#include "ofxNetwork.h"
#include "ofxOsc.h"
#include "ofxThread.h"
#include "ofxXmlSettings.h"
#include "ofx3DModelLoader.h"
#include "ofxONI.h"

#define MAX_PLAYERS 5
#define SAMPLE_XML_PATH "./Data/SamplesConfig.xml"
#define MAX_DEPTH 10000

static xn::Context g_Context;
static xn::DepthGenerator g_DepthGenerator;
static xn::UserGenerator g_UserGenerator;
static xn::ImageGenerator g_image;
static XnBool g_bNeedPose = FALSE;
static XnChar g_strPose[20] = "";
static float currentTime;
static float previousHeadX;
static float previousHeadY;
static string status;
static float x;
static enum CurrentSession{ NOT_IN_SESSION,IN_SESSION,USER_DETECTED,USER_LOST,POSE_DETECTED,CALIBRATION_STARTED} g_SessionState;
static XnFloat oniColors[][3] = {{0,1,1},{0,0,1},{0,1,0},{1,1,0},{1,0,0},{1,.5,0},{.5,1,0},{0,.5,1},{.5,0,1},{1,1,.5},{0,0,0}};
static XnUInt32 nColors = 10;
static ofx3DModelLoader squirrelModel;
static GLfloat lightOnePosition[] = {40.0, 40, 100.0, 0.0};
static GLfloat lightOneColor[] = {0.99, 0.99, 0.99, 1.0};
static GLfloat lightTwoPosition[] = {-40.0, 40, 100.0, 0.0};
static GLfloat lightTwoColor[] = {0.99, 0.99, 0.99, 1.0};

#define CHECK_RC(nRetVal, what)										\
	if (nRetVal != XN_STATUS_OK)									\
	{																\
		printf("%s failed: %s\n", what, xnGetStatusString(nRetVal));\
	}

//Callbacks
static void XN_CALLBACK_TYPE User_NewUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie)
{
	printf("New User %d\n", nId);
	// New user found
	if (g_bNeedPose)
	{
		g_UserGenerator.GetPoseDetectionCap().StartPoseDetection(g_strPose, nId);
		g_SessionState = USER_DETECTED;
	}
	else
	{
		g_UserGenerator.GetSkeletonCap().RequestCalibration(nId, TRUE);
	}
};
static void XN_CALLBACK_TYPE User_LostUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie)
{
	printf("Lost User id: %i\n", (unsigned int)nId);
	g_SessionState = USER_LOST;
};

// Callback: Detected a pose
static void XN_CALLBACK_TYPE UserPose_PoseDetected(xn::PoseDetectionCapability& capability, const XnChar* strPose, XnUserID nId, void* pCookie)
{
	printf("Pose %s detected for user %d\n", strPose, nId);
	g_UserGenerator.GetPoseDetectionCap().StopPoseDetection(nId);
	g_UserGenerator.GetSkeletonCap().RequestCalibration(nId, TRUE);
	g_SessionState = POSE_DETECTED;
};
// Callback: Started calibration
static void XN_CALLBACK_TYPE UserCalibration_CalibrationStart(xn::SkeletonCapability& capability, XnUserID nId, void* pCookie)
{
	printf("Calibration started for user %d\n", nId);
	g_SessionState = CALIBRATION_STARTED;
};
// Callback: Finished calibration
static void XN_CALLBACK_TYPE UserCalibration_CalibrationEnd(xn::SkeletonCapability& capability, XnUserID nId, XnBool bSuccess, void* pCookie)
{
	if (bSuccess)
	{
		// Calibration succeeded
		printf("Calibration complete, start tracking user %d\n", nId);
		g_UserGenerator.GetSkeletonCap().StartTracking(nId);
		g_SessionState = IN_SESSION;
	}
	else
	{
		// Calibration failed
		printf("Calibration failed for user %d\n", nId);
		g_SessionState = NOT_IN_SESSION;
		if (g_bNeedPose)
		{
			g_UserGenerator.GetPoseDetectionCap().StartPoseDetection(g_strPose, nId);
		}
		else
		{
			g_UserGenerator.GetSkeletonCap().RequestCalibration(nId, TRUE);
		}
	}
};
//Callbacks

static void PrintSessionState(CurrentSession eState)
{
	switch (eState)
	{

		case USER_DETECTED:
			status = "User Detected, Starting Pose Detection"; 
			//printf("User Detected, Starting Pose Detection\n");
			break;

		case POSE_DETECTED :
			status = "Pose Detected,Calibrating User Hands"; 
			//printf("Pose Detected,Calibrating User Hands\n");
			break;

		case CALIBRATION_STARTED :
			status = "Calibration Started"; 
			//printf("Calibration Started\n");
			break;

		case IN_SESSION:
			status = "Calibration Succeeded,Now Tracking hands";
			//printf("Calibration Succeeded,Now Tracking hands\n");
			break;

		case USER_LOST:
			status = "No User found";
			//printf("No User found\n");
			break;

		case NOT_IN_SESSION:
			status = "Start with Calibration Pose";
			//printf("Start with Calibration Pose\n");
			break;
	}
}

class ofxONI
{
    public:
        ofxONI();
        ~ofxONI();

		void setup();
		void update();

		
		void drawDepth(int x, int y) {drawDepth(x, y, width, height);};
		void drawDepth(int x, int y, int w, int h);
		void drawPlayers(int x, int y) {drawPlayers(x, y, width, height);};
		void drawPlayers(int x, int y, int w, int h);
		void drawCam(int x, int y) {drawCam(x, y, width, height);};
		void drawCam(int x, int y, int w, int h);

		void drawSkeletonPt(XnUserID player, XnSkeletonJoint eJoint);
		void drawSkeletonPoints(XnUserID player, XnSkeletonJoint eJoint);
		void skeletonTracking();

		xn::SceneMetaData sceneMD;
		xn::DepthMetaData depthMD;
		xn::ImageMetaData g_imageMD;

		ofxCvGrayscaleImage depth;
		ofxCvColorImage players;
		ofxCvColorImage imgCam;

		float depthHist[MAX_DEPTH];
		unsigned char gColorBuffer[640*480*3];         // BGRA

		unsigned char * tmpGrayPixels;
		unsigned char * tmpColorPixels;
		unsigned char * tmpCamColorPixels;

		XnPoint3D LHandPoint;
		XnPoint3D RHandPoint;
		XnPoint3D LastLHandPoint;
		XnPoint3D LastRHandPoint;
		XnPoint3D LastHeadPoint;
		XnPoint3D LastLeftHipPoint;	

		bool bDrawPlayers, bDrawCam;
		int width, height;
};



#endif

